package org.september.pisces.task.service;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;

import org.september.core.component.log.LogHelper;
import org.september.core.exception.BusinessException;
import org.september.pisces.task.entity.SimpleTask;
import org.september.pisces.task.enums.SimpleTaskStatus;
import org.september.smartdao.CommonDao;
import org.september.smartdao.model.ParamMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.stereotype.Service;

@Service
public class DynamicScheduledTaskService {
	
	protected LogHelper log = LogHelper.getLogger(this.getClass());
	
	private List<Long> runningTaskIds = new CopyOnWriteArrayList<>();
	
	@Autowired
	private SimpleTaskLogService simpleTaskLogService;
	
	@Autowired
	private CommonDao commonDao;
	
	@Value(value="${org.september.pisces.task.enable:true}")
	private boolean enable;
	
    private final DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar;
 
    public DynamicScheduledTaskService(@Value(value="${org.september.pisces.task.poolSize:16}") int poolSize) {
    	dynamicScheduledTaskRegistrar = new DynamicScheduledTaskRegistrar(poolSize);
    }
    
    public void 每天X点Y分运行(String taskName,int x,int y,Object instance , String methodName) {
    	String cron="* "+y+ " "+x+" * * ?";
    	String remark = "每天"+x+"点"+y+"分运行";
    	addAndUpdate(taskName,cron,remark,instance,methodName,true);
    }
    
    public void 每隔N秒运行(String taskName,int seconds,Object instance , String methodName) {
    	String cron="0/"+seconds+" * * * * ?";
    	addAndUpdate(taskName,cron,"每隔"+seconds+"秒運行一次",instance,methodName,true);
    }

    public void 从现在开始每隔N分钟运行(String taskName,int n,Object instance , String methodName) {
    	String cron="0 0/"+n+ " * * * ?";
    	String remark = "从现在开始每隔"+n+"分钟运行";
    	addAndUpdate(taskName,cron,remark,instance,methodName,true);
    }
    
    public void 从现在开始每隔X小时运行(String taskName,int x,Object instance , String methodName) {
    	String cron="0 0 0/"+x+" * * ?";
    	String remark = "从现在开始每隔"+x+"小时运行";
    	addAndUpdate(taskName,cron,remark,instance,methodName,true);
    }
    
    public void 每周X的Y点运行(String taskName,int x, int y,Object instance , String methodName) {
    	
    }
    
    public SimpleTask addAndUpdate(String taskName , String cron,String remark,Object instance , String methodName,boolean persist,Object... args) {
    	if(persist) {
    		SimpleTask taskPo = add(taskName, cron,instance,methodName,args);
        	if(taskPo!=null) {
        		taskPo.setRemark(remark);
        		taskPo.setCron(cron);
        		taskPo.setTaskName(taskName);
            	commonDao.update(taskPo);
        	}
        	return taskPo;
    	}else {
    		dynamicScheduledTaskRegistrar.addCronTask(taskName,cron,() -> {
				try {
					//是否在此节点运行定时任务。这种不持久化的任务，目前没有办法多节点竞争控制。
					log.getBuilder().info(taskName+" 任务由线程"+Thread.currentThread().getName()+"触发");
					if(enable==false) {
						log.getBuilder().info(0.01f, "定时任务未开启");
						return;
					}
					Method method = instance.getClass().getDeclaredMethod(methodName , toClassArray(args));
					method.invoke(instance,args);
				} catch (Exception e) {
					log.getBuilder().info("任務執行失敗",e);
				}
	        });
			return null;
    	}
    	
    }
    
    private Class<?>[] toClassArray(Object[] args){
    	Class<?>[] czz = new Class<?>[args.length];
    	for(int i=0;i<args.length;i++) {
    		czz[i] = args[i].getClass();
    	}
    	return czz;
    }
    /**
     * 新增任务
     * @param taskName
     * @param cron
     */
    private SimpleTask add(String taskName , String cron,Object instance , String methodName,Object... args){
    	if(enable==false) {
			log.getBuilder().info("定时任务未开启");
			return null;
		}
    	try {
    		//先添加到数据库
    		SimpleTask vo = new SimpleTask();
    		vo.setApi(instance.getClass().getName()+"."+methodName);
    		SimpleTask po = commonDao.getByExample(vo);
    		if(po==null) {
    			vo.setCron(cron);
    			vo.setTaskName(taskName);
				vo.setLocker("");
    			commonDao.save(vo);
    			po = vo;
    		}
			Method method = instance.getClass().getDeclaredMethod(methodName , toClassArray(args));
			final Boolean result = dynamicScheduledTaskRegistrar.addCronTask(taskName,cron,() -> {
				SimpleTask taskPo = null;
				try {
					//是否在此节点运行定时任务。
					log.getBuilder().info(taskName+" 任务由线程"+Thread.currentThread().getName()+"触发");
					if(enable==false) {
						log.getBuilder().info(0.01f, "定时任务未开启");
						return;
					}
					taskPo = lockAndGetTask(taskName);
					if(taskPo!=null) {
						if(taskPo.getRunStatus()==SimpleTaskStatus.已暂停.id) {
							log.getBuilder().info("任务"+taskName+"已经暂停");
							return;
						}else if(taskPo.getRunStatus()==SimpleTaskStatus.运行中.id) {
							if(runningTaskIds.contains(taskPo.getId())) {
								log.getBuilder().info("任务"+taskName+"正在运行");
								return;
							}
						}
						taskPo.setRunStatus(SimpleTaskStatus.运行中.id);
						commonDao.update(taskPo);
						runningTaskIds.add(taskPo.getId());
						method.invoke(instance,args);
						
						//运行完成更新数据库任务状态。
						taskPo.setLastExecuteTime(new Date());
						taskPo.setRunStatus(SimpleTaskStatus.待运行.id);
						taskPo.setLocker("");
						commonDao.update(taskPo);
						runningTaskIds.remove(taskPo.getId());
						
						simpleTaskLogService.addSuccessLog(taskPo);
					}else {
						log.getBuilder().info("任务"+taskName+"已经不存在,或者正在被其他节点锁定");
					}
					
				} catch (Exception e) {
					if(taskPo!=null) {
						simpleTaskLogService.addSuccessLog(taskPo);
					}
					throw new BusinessException("任務執行失敗",e);
				}
	        });
			log.getBuilder().info("定时任务添加结果：" + result);
			return po;
		} catch (Exception e) {
			throw new BusinessException("添加任務失敗",e);
		}
    	
        
    }
 
    private SimpleTask lockAndGetTask(String taskName) {
    	ParamMap pm = new ParamMap();
    	pm.put("taskName", taskName);
    	String locker = UUID.randomUUID().toString();
    	pm.put("locker", locker);
    	int total = commonDao.execute("SimpleTask.lockTask", pm);
    	if(total==1) {
    		SimpleTask vo = new SimpleTask();
    		vo.setTaskName(taskName);
    		vo.setRunStatus(SimpleTaskStatus.锁定中.id);
    		vo.setLocker(locker);
    		return commonDao.getByExample(vo);
    	}else {
    		return null;
    	}
    }
    /**
     * 刪除任务
     * @param taskName
     */
    public void delete(String taskName){
    	//TODO 數據庫刪除
        dynamicScheduledTaskRegistrar.cancelCronTask(taskName);
    }
 
    public void startHeartBeat() {
    	new Thread() {
    		public void run() {
    			while(true) {
    				try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
					}
    				Iterator<Long> it = runningTaskIds.iterator();
    				while(it.hasNext()) {
    					heartBeat(it.next());
    				}
    			}
    		}
    	}.start();
    }
    protected void heartBeat(Long id) {
    	ParamMap pm = new ParamMap();
    	pm.put("id", id);
    	pm.put("time", new Date());
    	commonDao.execute("SimpleTask.heartBeat", pm);
	}
    
    public void resetTaskPo() {
    	Calendar cal = Calendar.getInstance();
    	cal.add(Calendar.SECOND, -10);
    	ParamMap pm = new ParamMap();
    	pm.put("time", cal.getTime());
    	int total = commonDao.execute("SimpleTask.resetTask", pm);
    	log.getBuilder().info("已重置"+total+"个任务的状态");
    }

	private ScheduledFuture getFeature(ScheduledTask task) {
    	try {
    		Field field = task.getClass().getDeclaredField("future");
    		field.setAccessible(true);
    		return (ScheduledFuture) field.get(task);
    	}catch(Exception ex) {
    		return null;
    	}
    }
}
